package com.mk.juc.pool;

import lombok.extern.slf4j.Slf4j;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 线程池
 */
@Slf4j
public class ThreadPool {

    // 阻塞线程线程
    private final ThreadQueue<Thread> queue;
    // 线程集合
    private final Set<Worker> workers = new HashSet<>();
    // 核心线程数
    private final int coreSize;

    private final FullStrategy<Thread> strategy;

    public ThreadPool(ThreadQueue<Thread> queue, int coreSize, FullStrategy<Thread> strategy) {
        this.queue = queue;
        this.coreSize = coreSize;
        this.strategy = strategy;
    }

    public void execute(Runnable runnable, String name) {
        synchronized (workers) {
            Worker worker = new Worker(runnable, name);
            if (workers.size() < coreSize) {
                workers.add(worker);
                worker.start();
            } else {
//                queue.put(worker);
                // 优化为策略模式
                queue.tryPut(strategy, worker);
            }
        }
    }

    /**
     * 内部类
     */
    class Worker extends Thread {
        private String name;
        private Runnable runnable;

        public Worker(Runnable runnable, String name) {
            super(name);
            this.runnable = runnable;
        }

        @Override
        public void run() {
            // 执行任务
            // 1) 当 task 不为空，执行任务
            // 2) 当 task 执行完毕，再接着从任务队列获取任务并执行
            while (runnable != null || (runnable = queue.take(1, TimeUnit.SECONDS)) != null) {
                try {
                    runnable.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    runnable = null;
                }
            }
        }
    }

    public static void main(String[] args) {
        ThreadQueue<Thread> queue = new ThreadQueue<>(5);
        ThreadPool pool = new ThreadPool(queue, 2, (q, t) -> {log.info("任务{}放弃", t);});
        for (int i = 0; i < 50; i++) {
            int num = i;
            pool.execute(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info("---" + num);
            }, "t" + num);
        }

    }
}
